/*创建并使用属性文件*/

/*
myled_ctrl {
	status = "okay";
	compatible = "rk3399_led2_green";
	gpio_led = <&gpio1 23 GPIO_ACTIVE_HIGH>; // GPIO1_C7 
};	
*/

#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <dt-bindings/gpio/gpio.h>
#include <linux/slab.h>//kmalloc


/*定义结构体类型*/
struct rk3399_led
{
	unsigned int gpio;//引脚号
	int value;//引脚电平
	int validvalue;//引脚有效电平
};

/*全局变量*/
static struct rk3399_led* pled;//声明结构体指针，但没分配结构体变量空间

//定义id_table表
static const struct platform_device_id led_id_table[] = 
{
	{ .name = "rk3399_led", .driver_data = 0x1 }, 
	{ .name = "rk3288_led", .driver_data = 0x2 }, 
	{ .name = "rk3568_led", .driver_data = 0x3 },
};

//定义compatible匹配表
static const struct of_device_id led_match[] = 
{
	{ .compatible = "rk3399_led2_green", },
	{ .compatible = "rk3399_led2_orange", },
};


/*定义系统调用接口*/
//对应shell命令cat
ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf)
{	
	printk("<kernel>call function:%s\n",__FUNCTION__);
	return sprintf(buf, "%d\n", gpio_get_value(pled->gpio));
}

//对应shell命令echo
ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int ret;
	unsigned int val;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	ret = kstrtouint(buf, 10, &val); //把buf字符串转换为10进制的无符号整数，并写到val中
	if (ret)	return ret;

	if (val == 1) 
		gpio_direction_output(pled->gpio, pled->value);
	else if (val == 0) 
		gpio_direction_output(pled->gpio, !pled->value);
	else
		printk("Only support 0 or 1!\n");

	return count;
}


/*定义设备属性结构体变量*/
static DEVICE_ATTR(ledattr, 0664, led_show, led_store);//struct device_attribute dev_attr_ledattr

/*定义probe、remove函数*/
static int led_probe(struct platform_device *pdev)
{
	enum of_gpio_flags flags;
	struct device_node* pled_node = pdev->dev.of_node;
	printk("<kernel>call function:%s\n",__FUNCTION__);

	/*申请分配结构体空间*/
	pled = kmalloc(sizeof(struct rk3399_led), GFP_KERNEL);
	if(pled == NULL)
	{
		printk("kmalloc fail\n");
		return -ENOMEM;
	}

	/*获取LED灯引脚号和有效电平*/
	pled->gpio = of_get_named_gpio_flags(pled_node, "gpio_led", 0, &flags);
	if (gpio_is_valid(pled->gpio)) //如果获取到的引脚号是有效的
	{
		printk("<kernel>Get gpio of led success!\n");
		printk("<kernel>gpio_led = %d\n", pled->gpio);
		if(flags == GPIO_ACTIVE_LOW)
		{
			printk("<kernel>Low is valid.\n");
			pled->validvalue = 0;
		}
		else
		{
			printk("<kernel>High is valid.\n");
			pled->validvalue = 1;
		}
	} 
	else//如果获取到的引脚号是无效的
	{
		printk("<kernel>of_get_named_gpio_flags fail.\n");
		return -EFAULT;
	}	

	/*LED初始状态设置为关闭*/
	gpio_request(pled->gpio, "led2_green");
	gpio_direction_output(pled->gpio, !pled->validvalue);//将IO口设置为输出方向，初始为关灯状态
	gpio_free(pled->gpio);

	/*创建属性文件*/
	device_create_file(&pdev->dev, &dev_attr_ledattr);//创建名为ledattr的属性文件

	return 0;
}

static int led_remove(struct platform_device *pdev)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	
	/*LED最终状态设置为关闭*/
	gpio_request(pled->gpio, "led2_green");
	gpio_direction_output(pled->gpio, !pled->validvalue);
	gpio_free(pled->gpio);

	/*销毁属性文件*/
	device_remove_file(&pdev->dev, &dev_attr_ledattr);//销毁名为ledattr的属性文件

	/*释放堆区空间*/
	kfree(pled);	
	
	return 0;
}

/*定义平台驱动对象*/	
static struct platform_driver led_driver = 
{
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {.name	= "rk_led", .of_match_table = led_match,},
	.id_table	= led_id_table,
};


/*入口函数*/
static int __init led_init(void)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	return platform_driver_register(&led_driver);
}

/*出口函数*/
static void __exit led_exit(void)
{
	printk("<kernel>call function:%s\n",__FUNCTION__);
	platform_driver_unregister(&led_driver);
}


module_init(led_init); //告诉内核本模块程序的入口函数名
module_exit(led_exit);//告诉内核本模块程序的出口函数名
MODULE_LICENSE("GPL"); //遵守开源许可协议



